/*
function
go函数不支持嵌套、重载和默认参数
但支持一下特性：
	无需声明原型、不定长度变参、多返回值、命名返回值参数
	匿名函数、闭包
定义函数使用关键字func、且左大括号不能另起一行
函数也可以作为一种类型使用
*/
package main

import (
	"fmt"
)

func main() {
	fmt.Println("----------------Indefinite parameter-----------------")
	IndefiniteParameter(1)
	IndefiniteParameter(1, 2)
	fmt.Println("----------------copy value parameter-----------------")
	a1, a2 := 3, 4
	fmt.Println(a1, a2)
	CopyValueParameter(a1, a2)
	fmt.Println(a1, a2)
	fmt.Println("----------------slice parameter-----------------")
	b := []int{3, 4}
	fmt.Println(b)
	SliceParameter(b)
	fmt.Println(b)
	fmt.Println("----------------pointer parameter-----------------")
	c := 2
	fmt.Println(c)
	PointerParameter(&c)
	fmt.Println(c)
	fmt.Println("----------------func type-----------------")
	d := FuncType
	d()
	fmt.Println("----------------Anonymous function-----------------")
	/*
		匿名函数只能放在别的函数体中，不能作为最外层函数
	*/

	e := func() {
		fmt.Println("Anonymous function")
	}
	e()
	fmt.Println("----------------closure-----------------")
	f := Closure(10)
	fmt.Println(f(1))
	fmt.Println(f(2))
	fmt.Println("----------------defer-----------------")
	/*
	   defer
	   执行方式类似其它语言中的析构函数，在函数体执行结束后按照调用顺序的相反顺序逐个执行
	   即使函数发生严重错误也会执行
	   支持你明汗水的调用
	   常用于资源清理、文件关闭、解锁以及记录时间等操作
	   通过与匿名函数的配合可在return之后修改函数计算结果
	   如果函数体内某个变量作为defer时匿名函数的参数，则在定义defer时既已获得了拷贝，否则则是引用某个变量的地址

	   go没有异常机制，但有panic/recover模式来处理错误
	   panic可以在任何地方引发，但recover只有在defer调用的函数中有效
	*/
	Defer1()
	Defer2()
	Defer3()
	Defer4()
	PanicRecover()
	fmt.Println("----------------classroom work-----------------")
	var fs = [4]func(){}
	for i := 0; i < 4; i++ {
		defer fmt.Println("defer i= ", i)
		defer func() { fmt.Println("defer_closure i= ", i) }()
		fs[i] = func() { fmt.Println("closure i= ", i) } //定义时保留外层变量的地址，变量的值在被调用的时候确定
	}
	for _, f := range fs {
		f()
	}
}

/*
不定参函数
不定参数必须放在最后一个
*/
func IndefiniteParameter(a ...int) {
	fmt.Println("in func IndefiniteParameter:", a)
}

func CopyValueParameter(a ...int) {
	for i, _ := range a {
		a[i] = i
	}
	fmt.Println("in func CopyValueParameter:", a)
}

func SliceParameter(s []int) {
	for i, _ := range s {
		s[i] = i
	}
	fmt.Println("in func SliceParameter:", s)
}
func PointerParameter(i *int) {
	*i = 1
	fmt.Println("in func PointerParameter:", *i)
}

func FuncType() {
	fmt.Println("in func FuncType")
}
func Closure(x int) func(int) int {
	fmt.Println("in func Closure")
	fmt.Println(&x)
	return func(y int) int {
		fmt.Println("in func Closure(int).(int)int")
		fmt.Println(&x)
		return x + y
	}
}
func Defer1() {
	fmt.Println("in func Defer1")
	defer fmt.Println("b")
	defer fmt.Println("c")
}
func Defer2() {
	fmt.Println("in func Defer2")
	for i := 0; i < 3; i++ {
		defer fmt.Println(i)
	}
}
func Defer3() {
	fmt.Println("in func Defer3")
	for i := 0; i < 3; i++ {
		/*
			如果函数体内某个变量作为defer时匿名函数的参数，则在定义defer时既已获得了拷贝，否则则是引用某个变量的地址
			defer是在函数执行结束后逆向执行
		*/
		defer func(x int) {
			fmt.Println("in func Defer3().(int)")
			fmt.Println(x) //在定义函数(int)的时候拷贝i的值到x，x的值一次为0，1，2
		}(i)

	}
}
func Defer4() {
	fmt.Println("in func Defer4")
	for i := 0; i < 3; i++ {
		defer func() {
			fmt.Println("in func Defer4().()")
			fmt.Println(i) //在定义函数()的时候拷贝外层i的地址的值到内部的i，在函数执行结束后i的值变为3
		}()
	}
}
func PanicRecover() {
	fmt.Println("in func PanicRecover")
	defer func() {
		fmt.Println("in func PanicRecover().()")
		if err := recover(); err != nil {
			fmt.Println("recover in func PanicRecover().()")
		}
	}()
	panic("panic in PanicRecover") //panic发生后，下面的代码不会执行，所以defer recover 需要在panic之前定义
	fmt.Println("line after panic in PanicRecover")
}
